grammar Pascal;
options{
  output=AST;
  ASTLabelType=PascalNode;
  //debug
}
tokens{
  NUMBER_REAL;
  COMPOUND;
  NEGATE;
  NESTEXPR;
  LOOP;
  TEST;
  SELECT;
  SELECT_BRANCH;
  SELECT_CONSTANTS;
}
@lexer::header{
  package com.lifesting.book.wci;
}
@lexer::members{
public Token emit() {
  PascalAntlrToken ot = new PascalAntlrToken(input, state.type, state.channel,
                    state.tokenStartCharIndex, getCharIndex()-1);
    ot.setLine(state.tokenStartLine);
    ot.setText(state.text);
    ot.setCharPositionInLine(state.tokenStartCharPositionInLine);
    ot.setValue(ValueComputer.computerToken(ot));
    emit(ot);
    return ot;
 }
}
@parser::header{
  package com.lifesting.book.wci;
  
  import wci.intermediate.*;
}
@parser::members{
  
}
program: 
  compound_statement DOT!;
compound_statement: 
  BEGIN statement_list END ->^(COMPOUND statement_list);
assignment_statement:
 ID ASSIGN expression -> ^(ASSIGN ID expression);
statement:
  compound_statement | assignment_statement | control_statement;
control_statement:
  while_statement | repeat_statement | for_statement | if_statement | case_statement;
while_statement:
  WHILE t1=expression DO s1=statement ->^(LOOP ^(NOT $t1) $s1);
repeat_statement:
  REPEAT s1=statement_list UNTIL t1=expression ->^(LOOP $s1 $t1);
for_statement 
@init{
  PascalNode id_node = null;
  PascalNode id_cp1=null;
  PascalNode id_cp2=null;
  PascalNode id_cp3=null;
  PascalNode op_node=null;
  Object nested_node=null;
  boolean increment = true;
  PascalNode one_constant = (PascalNode)adaptor.create(NUMBER,"1");
  ((PascalAntlrToken)one_constant.getToken()).setValue(new Integer(1));
}:
  (FOR a1=assignment_statement {
          id_node =(PascalNode) ((PascalNode)a1.getTree()).getChild(0); 
          id_cp1 = (PascalNode)adaptor.create(id_node.getToken());
          id_cp2=  (PascalNode)adaptor.create(id_node.getToken());
          id_cp3=  (PascalNode)adaptor.create(id_node.getToken());
         }
    (TO{op_node=(PascalNode) adaptor.create(PLUS,"+");} | DOWNTO{increment=false;op_node=(PascalNode) adaptor.create(MINUS,"-");}) 
       ((e11=expression->{increment}?^(GT {id_cp1} $e11)->^(LT {id_cp1} $e11)){nested_node=retval.tree;})
              DO s2=statement) ->^(COMPOUND $a1 ^(LOOP ^(TEST {nested_node}) $s2 ^(ASSIGN {id_cp2} ^({op_node} {id_cp3} {one_constant}))));
if_statement:
  IF t1=expression THEN s1=statement ((ELSE)=>ELSE s2=statement)? -> ^(IF $t1 $s1 $s2?);
case_statement:
  CASE e1=expression OF bhs=case_branches END ->^(SELECT $e1 $bhs);
case_branches:
  case_branch (SEMI case_branch)* SEMI? -> case_branch+;
case_branch:
  c1=constant_list COLON s1=statement ->^(SELECT_BRANCH $c1 $s1);
constant_list:
  constant(COMMA constant)* -> ^(SELECT_CONSTANTS constant+);
constant:
  NUMBER | NUMBER_REAL | STRING;
statement_list:
  statement (SEMI statement)* SEMI? -> statement+;
expression: 
  simple_expression (rel_ops^ simple_expression)?;
rel_ops:
  LT | LE | GT | GE | NOT_EQUAL;
simple_expression: 
  signedterm (add_ops^ term)*;
signedterm:
  (a=PLUS | a=MINUS)? term ->{a!=null&&a.getType()==MINUS}?^(NEGATE term)->term;
add_ops:
  PLUS | MINUS | OR; 
term:
  factor (mul_ops^ factor)*;
mul_ops:
  STAR | SLASH | DIV | MOD | AND;
factor:
  ID | NUMBER | NUMBER_REAL | STRING | NOT^ factor | LPAREN expression RPAREN -> ^(NESTEXPR expression);

AND              : A N D	;
ARRAY            : A R R A Y	;
BEGIN            : B E G I N	;
CASE             : C A S E	;
CHAR		 : C H A R	;
CHR		 : C H R	;
CONST            : C O N S T	;
DIV              : D I V	;
DO               : D O		;
DOWNTO           : D O W N T O	;
ELSE             : E L S E	;
END              : E N  D	;
FILE             : F I L E	;
FOR              : F O R	;
FUNCTION         : F U N C T I O N;
GOTO             : G O T O	;
IF               : I F		;
IN               : I N		;
INTEGER          : I N T E G E R;
LABEL            : L A B E L	;
MOD              : M O D	;
NIL              : N I L	;
NOT              : N O T	;
OF               : O F		;
OR               : O R		;
PACKED           : P A C K E D	;
PROCEDURE        : P R O C E D U R E;
PROGRAM          : P R O G R A M;
REAL             : R E A L	;
RECORD           : R E C O R D	;
REPEAT           : R E P E A T	;
SET              : S E T	;
THEN             : T H E N	;
TO               : T O		;
TYPE             : T Y P E	;
UNTIL            : U N T I L	;
VAR              : V A R	;
WHILE            : W H I L E	;
WITH             : W I T H	;

PLUS            : '+'   ;
MINUS           : '-'   ;
STAR            : '*'   ;
SLASH           : '/'   ;
ASSIGN          : ':='  ;
COMMA           : ','   ;
SEMI            : ';'   ;
COLON           : ':'   ;
EQUAL           : '='   ;
NOT_EQUAL       : '<>'  ;
LT              : '<'   ;
LE              : '<='  ;
GE              : '>='  ;
GT              : '>'   ;
LPAREN          : '('   ;
RPAREN          : ')'   ;
LBRACK          : '['   ;
LBRACK2         : '(.'  ;
RBRACK          : ']'   ;
RBRACK2         : '.)'  ;
POINTER         : '^'   ;
AT              : '@'   ;
DOT             : '.' ;
DOTDOT          
	:	 '..' ;
LCURLY          : '{' ;
RCURLY          : '}' ;

ID  :	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
    ;
WS      : ( ' '
		|	'\t'
		|	'\f'
		|	(	'\r\n' 
			|	'\r'   
			|	'\n'   
			)
			{ 
			}
		)
		{ $channel=HIDDEN; }
	;
COMMENT
        :  '{'
           (
            :   '\r' '\n'     
		    |	'\r'			
		    |	'\n'
            |   ~('}' | '\n' | '\r')
		    )*
           '}'
		{$channel=HIDDEN;}
	;
STRING
	: '\'' ('\'\'' | ~('\''))* '\''	;
NUMBER
	:	('0'..'9')+ 
		(	(	{(input.LA(2)!='.')&&(input.LA(2)!=')')}?		
				'.' {$type = NUMBER_REAL;}	
				('0'..'9')+ (EXPONENT)?
			)?
		|	EXPONENT {$type= NUMBER_REAL;}	
		)
	;
fragment
EXPONENT
	:	('e') ('+'|'-')? ('0'..'9')+
	;

fragment A:('a'|'A');
fragment B:('b'|'B');
fragment C:('c'|'C');
fragment D:('d'|'D');
fragment E:('e'|'E');
fragment F:('f'|'F');
fragment G:('g'|'G');
fragment H:('h'|'H');
fragment I:('i'|'I');
fragment J:('j'|'J');
fragment K:('k'|'K');
fragment L:('l'|'L');
fragment M:('m'|'M');
fragment N:('n'|'N');
fragment O:('o'|'O');
fragment P:('p'|'P');
fragment Q:('q'|'Q');
fragment R:('r'|'R');
fragment S:('s'|'S');
fragment T:('t'|'T');
fragment U:('u'|'U');
fragment V:('v'|'V');
fragment W:('w'|'W');
fragment X:('x'|'X');
fragment Y:('y'|'Y');
fragment Z:('z'|'Z');